const Joi = require('joi');
const Jwt = require('../utils/jwt');
const encryption = require('../utils/encryption.js');
const UserModel = require('../models/userModel.js');

const passReg = new RegExp('^[a-zA-Z0-9]{6,30}$');
const JOI_USERNAME = Joi.string().alphanum().min(4).max(30).required().messages({
  'string.base': '字段必须为字符串',
  'string.alphanum': '只能包含字母和数字',
  'string.min': '长度不能少于4个字符',
  'string.max': '长度不能超过30个字符',
  'any.required': '该字段为必填项'
});
const JOI_PASSWORD = Joi.string().pattern(passReg).required().messages({
  'string.base': '密码必须是字符串',
  'string.empty': '密码不能为空',
  'string.pattern.base': '密码必须为6-30位字母或数字组合',
  'any.required': '密码是必填字段'
})

class ValidationMiddleware {
  /**
   * 登录验证
   * @param {Object} ctx 
   * @param {Function} next 
   * @returns 
   */
  login(ctx, next) {
    const { username, password } = ctx.request.body;
    const schema = Joi.object({
      username: JOI_USERNAME,
      password: JOI_PASSWORD
    });
    const { error } = schema.validate({ username, password: encryption.decryptAES(password) });

    if (error) {
      ctx.status = 400;
      ctx.body = {
        success: false,
        message: error.details[0].message
      };
      return;
    }

    return next();
  }

  /**
   * 注册验证
   * @param {Object} ctx 
   * @param {Function} next 
   * @returns 
   */
  register(ctx, next) {
    const schema = Joi.object({
      username: JOI_USERNAME,
      email: Joi.string().email().required(),
      password: JOI_PASSWORD,
      confirmPassword: Joi.ref('password')
    }).with('password', 'confirmPassword');

    const { error } = schema.validate(ctx.request.body);

    if (error) {
      ctx.status = 400;
      ctx.body = {
        success: false,
        message: error.details[0].message
      };
      return;
    }

    return next();
  }

  /**
   * 原密码有效性
   * @param {*} ctx 
   * @param {*} next 
   */
  async OriginalPassValidity(ctx, next) {
    const { username, password } = ctx.request.body;
    const user = await UserModel.findByUsername(username);
    const isPasswordValid = await encryption.comparePassword(
      encryption.decryptAES(password), user.password);

    if (!user) {
      ctx.status = 400;
      ctx.body = { success: false, message: '用户不存在' };
      return;
    }

    if (!isPasswordValid) {
      ctx.status = 400;
      ctx.body = { success: false, message: '原密码不匹配' };
      return;
    }

    await next();
  }

  /**
   * 新密码合法性
   * @param {*} ctx 
   * @param {*} next 
   */
  async newPassLegitimacy(ctx, next) {
    const { username, newPassword, confirmPassword } = ctx.request.body;

    /**
     * 1.新原密码是否相同
     */
    const user = await UserModel.findByUsername(username);
    const isNewPasswordValid = await encryption.comparePassword(
      encryption.decryptAES(newPassword), user.password);
    if (isNewPasswordValid) {
      ctx.status = 400;
      ctx.body = { success: false, message: '新密码与原密码不能相同' };
      return;
    }

    /**
     * 2.新密码是否合法
     */
    const schema = Joi.object({
      newPassword: JOI_PASSWORD
    });
    const { error } = schema.validate({ newPassword: encryption.decryptAES(newPassword) });
    if (error) {
      ctx.status = 400;
      ctx.body = { success: false, message: '新密码不合法' };
      return;
    }

    /**
     * 3.新密码确认密码是否一致
     */
    if (newPassword !== confirmPassword) {
      ctx.status = 400;
      ctx.body = { success: false, message: '新密码与确认密码不一致' };
      return;
    }
    await next();
  }

  /**
   * JWT 认证中间件
   * @param {Object} req 
   * @param {Function} next 
   * @returns 
   */
  async authenticateToken(ctx, next) {
    const authHeader = ctx.headers.authorization;
    const token = authHeader && authHeader.split(' ')[1];

    if (!token) {
      ctx.status = 401;
      ctx.body = { message: '访问被拒绝，缺少 token' };
      return;
    }

    try {
      const decoded = Jwt.verifyToken(token, process.env.JWT_SECRET);
      ctx.state.user = decoded;

      const currentTime = Math.floor(Date.now() / 1000); // 当前时间戳（秒）
      const timeDiff = decoded.exp - currentTime; // 剩余有效时间（秒）
      const refreshThreshold = 2 * 60 * 60; // 2h阈值（单位：秒）

      // 如果剩余时间小于阈值，生成新token
      if (timeDiff > 0 && timeDiff < refreshThreshold) {
        // 使用原用户信息生成新token，保持相同有效期
        const newToken = Jwt.generateToken(
          { userId: decoded.userId, username: decoded.username },
          { expiresIn: process.env.JWT_EXPIRES_IN } // 如 '1h'
        );
        // 将新token添加到响应头
        ctx.set('X-Refresh-Token', newToken);
      }

      await next();
    } catch (error) {
      ctx.status = 401;
      ctx.body = { message: 'Token 无效或已过期' };
    }
  };

  async addUser(ctx, next) {
    const { username } = ctx.request.body;
    const schema = Joi.object({
      username: JOI_USERNAME,
    });
    const { error } = schema.validate({ username });

    if (error) {
      ctx.status = 400;
      ctx.body = {
        success: false,
        message: error.details[0].message
      };
      return;
    }

    const user = await UserModel.findByUsername(username);
    if (user) {
      ctx.status = 409;
      ctx.body = { success: false, message: '用户已存在，请设置其他用户名' };
      return;
    }
    await next();
  }
}

module.exports = new ValidationMiddleware();

function formatISODateToStandard(isoString) {
  const date = new Date(isoString);

  // 获取各时间组件并补零
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');

  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}